#include <string.h>
#include <targets/lpc2378.h>

#include "mci.h"
#include "diskio.h"
#include "../drivers/timer.h"

#define MCI_STATUS MCIStatus

/* For the SD card I tested, the minimum required block length is 512 */
/* For MMC, the restriction is loose, due to the variety of SD and MMC
card support, ideally, the driver should read CSD register to find the 
right speed and block length for the card, and set them accordingly.
In this driver example, it will support both MMC and SD cards, and it
does read the information by send SEND_CSD to poll the card status,
however, to simplify the example, it doesn't configure them accordingly
based on the CSD register value. This is not intended to support all 
the SD and MMC cards. */


static DSTATUS Stat;

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE drv		// Physical drive nmuber (0) 
)
{

  if (drv != 0) return STA_NOINIT;			// Supports only single drive
  if (Stat & STA_NODISK) return Stat;	// No card in the socket

  MCI_Init();
  delay_ms(50);

  if ( (MCI_CardType = MCI_CardInit()) == CARD_UNKNOWN )
  {
    MCI_PowerDown();
    return STA_NOINIT;
  }
  else if(MCI_CardType == MMC_CARD) 
  {
    MCI_PowerDown();
    return STA_NOINIT;				// MicroSD socket should not accept MMC :) 
  }
  else
  {
    //SD card detected...
  }
  delay_ms(20);

  if ( MCI_Check_CID() != (1) )
  {
    MCI_PowerDown();
    return (Stat |= STA_NOINIT);
  }

  if ( MCI_Set_Address() != (1) )
  {
    MCI_PowerDown();
    return (Stat |= STA_NOINIT);
  }

  if ( MCI_Send_CSD() != (1) )
  {
    MCI_PowerDown();
    return (Stat |= STA_NOINIT);
  }

  if ( MCI_Select_Card() != (1) )
  {
    MCI_PowerDown();
    return (Stat |= STA_NOINIT);
  }

  if ( MCI_CardType == SD_CARD )
  {
    MCI_Set_MCIClock(NORMAL_RATE);
    if ( MCI_Send_ACMD_Bus_Width( SD_4_BIT ) == (0) )
    {
      MCI_PowerDown();
      return (Stat |= STA_NOINIT);
    }
  }

  delay_ms(20);

  if ( MCI_Set_BlockLen( BLOCK_LENGTH ) != (1) )
  {
    MCI_PowerDown();
    return (Stat |= STA_NOINIT);
  }

  Stat &= ~STA_NOINIT;		/* Clear STA_NOINIT */
  return Stat;
}

/*-----------------------------------------------------------------------*/
/* Get Disk Status                                                       */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE drv		/* Physical drive nmuber (0) */
)
{
  if (drv) return STA_NOINIT;		/* Supports only single drive */
  return Stat;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE drv,			/* Physical drive nmuber (0) */
	BYTE *buff,			/* Pointer to the data buffer to store read data */
	DWORD sector,		/* Start sector number (LBA) */
	BYTE count			/* Sector count (1..255) */
)
{
  //disk_check_sd();
  if (drv || !count) 
    return RES_PARERR;
  if (Stat & STA_NOINIT) 
    return RES_NOTRDY;
  
  //deselect, select
  
  do
  {
            
    //ctl_mask_isr(VICIntSelect_UART1_BIT);
    if ( MCI_Read_Block(sector) != (1) )
    {
      //ctl_unmask_isr(VICIntSelect_UART1_BIT);
            break;
    }
    //ctl_unmask_isr(VICIntSelect_UART1_BIT);
  
    //while ( MCI_STATUS & MCI_RX_ACTIVE ); 
    //while (MCI_Block_End_Flag == 1);
    //MCI_RXDisable();
  
    memcpy(buff, (unsigned char*)ReadBlock, 512); buff+=512;
    
    sector++;
  }while(--count);

//release spi

  return count ? RES_ERROR : RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_write (
	BYTE drv,			/* Physical drive nmuber (0) */
	const BYTE *buff,	/* Pointer to the data to be written */
	DWORD sector,		/* Start sector number (LBA) */
	BYTE count			/* Sector count (1..255) */
)
{
        //disk_check_sd();
	if (drv || !count) 
          return RES_PARERR;
	if (Stat & STA_NOINIT) 
          return RES_NOTRDY;

	do {
          memcpy((unsigned char*)WriteBlock, buff, 512); 
          buff+=512;
          //ctl_mask_isr(VICIntSelect_UART1_BIT);
          if (MCI_Write_Block(sector) != (1)) {
            //ctl_unmask_isr(VICIntSelect_UART1_BIT);
            break;
          }
          //ctl_unmask_isr(VICIntSelect_UART1_BIT);
          sector++;
          
	} while(--count);

	return count ? RES_ERROR : RES_OK;
}

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE drv,		/* Physical drive nmuber (0) */
	BYTE ctrl,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;

	if (drv) return RES_PARERR;
	if (Stat & STA_NODISK) return RES_NOTRDY;
	if (Stat & STA_NOINIT) return RES_NOTRDY;

	res = RES_ERROR;

	switch (ctrl) 
	{
	case CTRL_SYNC :		// Make sure that no pending write process.
		res = RES_OK;		// We have buffer flushed inside disk_write() function.
		break;
	case GET_SECTOR_COUNT :	// Get number of sectors on the disk (DWORD)
//		res = RES_ERROR;	// Not needed for now, as f_mkfs() not used
                *(DWORD*)buff = 3500000;  // фиксорованная длина, вообще надо вынимать данные из свойств SD_карты
		res = RES_OK;
		break;
	case GET_SECTOR_SIZE :	// Get R/W sector size (WORD)
		*(WORD*)buff = 512;	// Hardcoded 512
		res = RES_OK;
		break;
	case GET_BLOCK_SIZE :	// Get erase block size in unit of sector (DWORD)
		res = RES_ERROR;	// Not needed for SD/MMC (_USE_ERASE = 0)
//                *(DWORD*)buff = 1;  // размер блока для флешпамяти, нафиг не надо
//		res = RES_OK;
		break;
	default:
		res = RES_PARERR;
	}
	return res;
}


DWORD get_fattime (void);
DWORD get_fattime (void)
{
	/* Pack date and time into a DWORD variable */
	return	  ((DWORD)(2010 - 1980) << 25)	// year
			| ((DWORD)11 << 21)				// month
			| ((DWORD)9 << 16)				// day
			| ((DWORD)0 << 11)				// hour
			| ((DWORD)0 << 5)				// min
			| ((DWORD)0 >> 1);				// sec
}

/* Check card presence within timer process */
void disk_check_sd(void)
{
  if (DISK_SD_INSERTED) {
    Stat &= ~STA_NODISK;
  }
  else {
    Stat |= (STA_NODISK | STA_NOINIT);
  }
  //return DISK_SD_INSERTED;
}